<template>
    <view>
        <view ref="uni-rate" class="uni-rate">
            <view
                v-for="(star, index) in stars"
                :key="index"
                class="uni-rate__icon"
                :class="{ 'uni-cursor-not-allowed': disabled }"
                :style="{ 'margin-right': marginNumber + 'px' }"
                @touchstart.stop="touchstart"
                @touchmove.stop="touchmove"
                @mousedown.stop="mousedown"
                @mousemove.stop="mousemove"
                @mouseleave="mouseleave"
            >
                <uni-icons :color="color" :size="size" :type="isFill ? 'star-filled' : 'star'" />
                <!-- #ifdef APP-NVUE -->
                <view
                    :style="{ width: (star.activeWitch.replace('%', '') * size) / 100 + 'px' }"
                    class="uni-rate__icon-on"
                >
                    <uni-icons
                        style="text-align: left"
                        :color="disabled ? '#ccc' : activeColor"
                        :size="size"
                        type="star-filled"
                    />
                </view>
                <!-- #endif -->
                <!-- #ifndef APP-NVUE -->
                <view :style="{ width: star.activeWitch }" class="uni-rate__icon-on">
                    <uni-icons :color="disabled ? disabledColor : activeColor" :size="size" type="star-filled" />
                </view>
                <!-- #endif -->
            </view>
        </view>
    </view>
</template>

<script>
// #ifdef APP-NVUE
const dom = uni.requireNativePlugin("dom");
// #endif
/**
 * Rate 评分
 * @description 评分组件
 * @tutorial https://ext.dcloud.net.cn/plugin?id=33
 * @property {Boolean} 	isFill = [true|false] 		星星的类型，是否为实心类型, 默认为实心
 * @property {String} 	color 						未选中状态的星星颜色，默认为 "#ececec"
 * @property {String} 	activeColor 				选中状态的星星颜色，默认为 "#ffca3e"
 * @property {String} 	disabledColor 				禁用状态的星星颜色，默认为 "#c0c0c0"
 * @property {Number} 	size 						星星的大小
 * @property {Number} 	value/v-model 				当前评分
 * @property {Number} 	max 						最大评分评分数量，目前一分一颗星
 * @property {Number} 	margin 						星星的间距，单位 px
 * @property {Boolean} 	disabled = [true|false] 	是否为禁用状态，默认为 false
 * @property {Boolean} 	readonly = [true|false] 	是否为只读状态，默认为 false
 * @property {Boolean} 	allowHalf = [true|false] 	是否实现半星，默认为 false
 * @property {Boolean} 	touchable = [true|false] 	是否支持滑动手势，默认为 true
 * @event {Function} change 						uniRate 的 value 改变时触发事件，e={value:Number}
 */

export default {
    name: "UniRate",
    props: {
        isFill: {
            // 星星的类型，是否镂空
            type: [Boolean, String],
            default: true
        },
        color: {
            // 星星未选中的颜色
            type: String,
            default: "#ececec"
        },
        activeColor: {
            // 星星选中状态颜色
            type: String,
            default: "#ffca3e"
        },
        disabledColor: {
            // 星星禁用状态颜色
            type: String,
            default: "#c0c0c0"
        },
        size: {
            // 星星的大小
            type: [Number, String],
            default: 24
        },
        value: {
            // 当前评分
            type: [Number, String],
            default: 0
        },
        modelValue: {
            // 当前评分
            type: [Number, String],
            default: 0
        },
        max: {
            // 最大评分
            type: [Number, String],
            default: 5
        },
        margin: {
            // 星星的间距
            type: [Number, String],
            default: 0
        },
        disabled: {
            // 是否可点击
            type: [Boolean, String],
            default: false
        },
        readonly: {
            // 是否只读
            type: [Boolean, String],
            default: false
        },
        allowHalf: {
            // 是否显示半星
            type: [Boolean, String],
            default: false
        },
        touchable: {
            // 是否支持滑动手势
            type: [Boolean, String],
            default: true
        }
    },
    data() {
        return {
            valueSync: "",
            userMouseFristMove: true,
            userRated: false,
            userLastRate: 1
        };
    },
    computed: {
        stars() {
            const value = this.valueSync ? this.valueSync : 0;
            const starList = [];
            const floorValue = Math.floor(value);
            const ceilValue = Math.ceil(value);
            for (let i = 0; i < this.max; i++) {
                if (floorValue > i) {
                    starList.push({
                        activeWitch: "100%"
                    });
                } else if (ceilValue - 1 === i) {
                    starList.push({
                        activeWitch: (value - floorValue) * 100 + "%"
                    });
                } else {
                    starList.push({
                        activeWitch: "0"
                    });
                }
            }
            return starList;
        },

        marginNumber() {
            return Number(this.margin);
        }
    },
    watch: {
        value(newVal) {
            this.valueSync = Number(newVal);
        },
        modelValue(newVal) {
            this.valueSync = Number(newVal);
        }
    },
    created() {
        this.valueSync = Number(this.value || this.modelValue);
        this._rateBoxLeft = 0;
        this._oldValue = null;
    },
    mounted() {
        setTimeout(() => {
            this._getSize();
        }, 100);
        // #ifdef H5
        this.PC = this.IsPC();
        // #endif
    },
    methods: {
        touchstart(e) {
            // #ifdef H5
            if (this.IsPC()) return;
            // #endif
            if (this.readonly || this.disabled) return;
            const { clientX, screenX } = e.changedTouches[0];
            // TODO 做一下兼容，只有 Nvue 下才有 screenX，其他平台式 clientX
            this._getRateCount(clientX || screenX);
        },
        touchmove(e) {
            // #ifdef H5
            if (this.IsPC()) return;
            // #endif
            if (this.readonly || this.disabled || !this.touchable) return;
            const { clientX, screenX } = e.changedTouches[0];
            this._getRateCount(clientX || screenX);
        },

        /**
         * 兼容 PC @tian
         */

        mousedown(e) {
            // #ifdef H5
            if (!this.IsPC()) return;
            if (this.readonly || this.disabled) return;
            const { clientX } = e;
            this.userLastRate = this.valueSync;
            this._getRateCount(clientX);
            this.userRated = true;
            // #endif
        },
        mousemove(e) {
            // #ifdef H5
            if (!this.IsPC()) return;
            if (this.userRated) return;
            if (this.userMouseFristMove) {
                console.log("---mousemove----", this.valueSync);
                this.userLastRate = this.valueSync;
                this.userMouseFristMove = false;
            }
            if (this.readonly || this.disabled || !this.touchable) return;
            const { clientX } = e;
            this._getRateCount(clientX);
            // #endif
        },
        mouseleave(e) {
            // #ifdef H5
            if (!this.IsPC()) return;
            if (this.readonly || this.disabled || !this.touchable) return;
            if (this.userRated) {
                this.userRated = false;
                return;
            }
            this.valueSync = this.userLastRate;
            // #endif
        },
        // #ifdef H5
        IsPC() {
            var userAgentInfo = navigator.userAgent;
            var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
            var flag = true;
            for (let v = 0; v < Agents.length - 1; v++) {
                if (userAgentInfo.indexOf(Agents[v]) > 0) {
                    flag = false;
                    break;
                }
            }
            return flag;
        },
        // #endif

        /**
         * 获取星星个数
         */
        _getRateCount(clientX) {
            this._getSize();
            const size = Number(this.size);
            if (isNaN(size)) {
                return new Error("size 属性只能设置为数字");
            }
            const rateMoveRange = clientX - this._rateBoxLeft;
            let index = parseInt(rateMoveRange / (size + this.marginNumber));
            index = index < 0 ? 0 : index;
            index = index > this.max ? this.max : index;
            const range = parseInt(rateMoveRange - (size + this.marginNumber) * index);
            let value = 0;
            if (this._oldValue === index && !this.PC) return;
            this._oldValue = index;
            if (this.allowHalf) {
                if (range > size / 2) {
                    value = index + 1;
                } else {
                    value = index + 0.5;
                }
            } else {
                value = index + 1;
            }

            value = Math.max(0.5, Math.min(value, this.max));
            this.valueSync = value;
            this._onChange();
        },

        /**
         * 触发动态修改
         */
        _onChange() {
            this.$emit("input", this.valueSync);
            this.$emit("update:modelValue", this.valueSync);
            this.$emit("change", {
                value: this.valueSync
            });
        },
        /**
         * 获取星星距离屏幕左侧距离
         */
        _getSize() {
            // #ifndef APP-NVUE
            uni.createSelectorQuery()
                .in(this)
                .select(".uni-rate")
                .boundingClientRect()
                .exec((ret) => {
                    if (ret) {
                        this._rateBoxLeft = ret[0].left;
                    }
                });
            // #endif
            // #ifdef APP-NVUE
            dom.getComponentRect(this.$refs["uni-rate"], (ret) => {
                const size = ret.size;
                if (size) {
                    this._rateBoxLeft = size.left;
                }
            });
            // #endif
        }
    }
};
</script>

<style lang="scss">
.uni-rate {
    /* #ifndef APP-NVUE */
    display: flex;
    /* #endif */
    line-height: 1;
    font-size: 0;
    flex-direction: row;
    /* #ifdef H5 */
    cursor: pointer;
    /* #endif */
}

.uni-rate__icon {
    position: relative;
    line-height: 1;
    font-size: 0;
}

.uni-rate__icon-on {
    overflow: hidden;
    position: absolute;
    top: 0;
    left: 0;
    line-height: 1;
    text-align: left;
}

.uni-cursor-not-allowed {
    /* #ifdef H5 */
    cursor: not-allowed !important;
    /* #endif */
}
</style>
